package com.fox.application.service;

import com.fox.application.dto.CreateOrderCommand;
import com.fox.domain.model.Money;
import com.fox.domain.model.Order;
import com.fox.domain.model.OrderItem;
import com.fox.domain.repository.OrderRepository;
import com.fox.domain.service.DiscountPolicy;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.UUID;

/**
 * 应用服务：负责下单流程编排
 * OrderApplicationService 处于应用层，承担用例编排责任。它将“输入的 DTO”→“领域模型”→“调用领域行为”→“调用仓储保存”→“发布领域事件”等完整流程串联起来。
 * 业务逻辑（如计算总金额、状态校验）都在领域模型（Entity、领域服务）里完成；应用层只做“流程控制”，并保证事务边界在合适的位置。
 */
public class OrderApplicationService {

    private final OrderRepository orderRepository;      // 注入仓储接口
    private final InventoryService inventoryService;    // 外部库存服务接口
    private final DiscountPolicy discountPolicy;        // 领域服务
    private final DomainEventPublisher eventPublisher;  // 领域事件发布器

    public OrderApplicationService(OrderRepository orderRepository,
                                   InventoryService inventoryService,
                                   DiscountPolicy discountPolicy,
                                   DomainEventPublisher eventPublisher) {
        this.orderRepository = orderRepository;
        this.inventoryService = inventoryService;
        this.discountPolicy = discountPolicy;
        this.eventPublisher = eventPublisher;
    }

    @Transactional
    public UUID createOrder(CreateOrderCommand cmd) {
        // 1. 将 DTO 转为领域模型对象
        List<OrderItem> items = new ArrayList<>();
        for (CreateOrderCommand.OrderItemDto dto : cmd.getItems()) {
            Money price = new Money(dto.getUnitPrice(), java.util.Currency.getInstance("CNY"));
            OrderItem item = new OrderItem(dto.getProductId(), dto.getQuantity(), price);
            items.add(item);
        }
        // 2. 新建聚合根 Order，自动计算总价（初始不含优惠）
        Order order = new Order(cmd.getUserId(), items);

        // 3. 应用优惠策略
        order.applyDiscount(discountPolicy);

        // 4. 扣库存：由于库存属于另一个限界上下文，需要先调用外部服务
        //    先将订单状态标记为 PENDING_STOCK_CHECK
        order.markPendingStockCheck();
        for (OrderItem item : items) {
            boolean ok = inventoryService.checkAndReduceStock(item.getProductId(), item.getQuantity());
            if (!ok) {
                throw new RuntimeException("库存不足：" + item.getProductId());
            }
        }

        // 5. 库存校验通过，标记订单已支付
        order.markPaid();

        // 6. 保存订单（写入数据库）
        orderRepository.save(order);

        // 7. 发布领域事件（比如通知下游系统）
        OrderCreatedEvent event = new OrderCreatedEvent(order.getId(), order.getUserId(), order.getTotalAmount());
        eventPublisher.publish(event);

        return order.getId();
    }

    // 其他用例：支付回调、取消订单等，都由 ApplicationService 编排
}